/*******************************************************************************
 * Copyright 2009 Dop Sun
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
/**
 * Place holder for copyright, needs to be replaced before release.
 */
package com.softao.ming.gde.internal.ui.launcher;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipFile;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceContainerType;
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
import org.eclipse.debug.core.sourcelookup.containers.ArchiveSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.ExternalArchiveSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.WorkspaceSourceContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.sourcelookup.containers.JavaSourceLookupParticipant;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;

import com.softao.ming.gde.internal.core.launcher.LaunchUtil;
import com.softao.ming.gde.internal.core.natures.GdePluginFile;
import com.softao.ming.gde.internal.ui.GdeUI;
import com.softao.ming.gde.internal.ui.preferences.InstalledGwt;
import com.softao.ming.gde.internal.ui.preferences.InstalledGwt.GwtDefinition;

/**
 * 
 */
public class GwtSourceLookupDirector extends AbstractSourceLookupDirector {
	private static Set<String> fFilteredTypes;
	
	static {
		fFilteredTypes = new HashSet<String>(3);
		fFilteredTypes.add(ProjectSourceContainer.TYPE_ID);
		fFilteredTypes.add(WorkspaceSourceContainer.TYPE_ID);
		// can't reference UI constant
		fFilteredTypes.add("org.eclipse.debug.ui.containerType.workingSet"); //$NON-NLS-1$
	}

	@SuppressWarnings("unused")
	private final ILaunch mLaunch;
	private final ILaunchConfiguration mConfiguration;

	/**
	 * @param launch 
	 * @param configuration 
	 */
	public GwtSourceLookupDirector(ILaunch launch,
			ILaunchConfiguration configuration) {
		super();
		mLaunch = launch;
		mConfiguration = configuration;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.sourcelookup.ISourceLookupDirector#initializeParticipants()
	 */
	public void initializeParticipants() {
		addParticipants(new ISourceLookupParticipant[] {new JavaSourceLookupParticipant()});
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.internal.core.sourcelookup.ISourceLookupDirector#supportsSourceContainerType(org.eclipse.debug.internal.core.sourcelookup.ISourceContainerType)
	 */
	public boolean supportsSourceContainerType(ISourceContainerType type) {
		return !fFilteredTypes.contains(type.getId());
	}
	
	@Override
	public void setSourceContainers(ISourceContainer[] containers) {
		List<ISourceContainer> result = new ArrayList<ISourceContainer>();
		for (ISourceContainer container : containers) {
			result.add(container);
		}
		for (ISourceContainer container : getGDESourceContainers()) {
			result.add(container);
		}
		super.setSourceContainers(result.toArray(new ISourceContainer[result.size()]));
	}

	ISourceContainer[] getGDESourceContainers() {
		List<ISourceContainer> result = new ArrayList<ISourceContainer>();
		for (IPluginModelBase plugin : PluginRegistry.getActiveModels()) {
			IResource resource = plugin.getUnderlyingResource();
			IProject project = resource == null ? null : resource.getProject();
			if (project != null) {
				List<IRuntimeClasspathEntry> pathList = new ArrayList<IRuntimeClasspathEntry>();
				try {
					addProjectSourceContainers(project, pathList);
				} catch (CoreException e) {
					e.printStackTrace();
				}
				IRuntimeClasspathEntry[] entries = (IRuntimeClasspathEntry[]) pathList.toArray(new IRuntimeClasspathEntry[pathList.size()]);
				ISourceContainer[] containers = JavaRuntime.getSourceContainers(entries);
				for (ISourceContainer container : containers) {
					result.add(container);
				}
			} else {
				String installLocation = plugin.getInstallLocation();
				File file = new File(installLocation);
				if (file.isFile()) {
					try {
						ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ);
						if (zipFile.getEntry(GdePluginFile.FILE_NAME) != null) {
							ISourceContainer container = getArchiveSourceContainer(file.toString());
							if (container != null) {
								result.add(container);
							}
						}
					} catch (JavaModelException e) {
						e.printStackTrace();
						continue;
					} catch (IOException e) {
						e.printStackTrace();
						continue;
					}
				} else {
					File gwtPluginFile = new File(file, GdePluginFile.FILE_NAME);
					if (!gwtPluginFile.exists()) {
						continue;
					}
					IPath path = GdeUI.getWorkspace().getRoot().getLocation();
					IPath filePath = new Path(file.toString());
					if (path.isPrefixOf(filePath)) {
						ISourceContainer container = new DirectorySourceContainer(new Path(file.toString()), true);
						result.add(container);
					} else {
						ISourceContainer container = new DirectorySourceContainer(new Path(file.toString()), true);
						result.add(container);
					}
				}
			}
		}
		
		String workspaceDataLocation = LaunchUtil.getWorkspaceDataLocation(mConfiguration);
		ISourceContainer container = new DirectorySourceContainer(new Path(workspaceDataLocation), true);
		result.add(container);
		
		List<GwtDefinition> list = InstalledGwt.load();
		for (GwtDefinition gwtDefinition : list) {
			if (gwtDefinition.isDefault()) {
				File gwtFile = new File(gwtDefinition.getPath());
				ISourceContainer gwtContainer;
				try {
					gwtContainer = getArchiveSourceContainer((new File(gwtFile, "gwt-dev-windows.jar")).toString());
				} catch (JavaModelException e) {
					e.printStackTrace();
					break;
				}
				if (gwtContainer != null) {
					result.add(gwtContainer);
				}
				
				try {
					gwtContainer = getArchiveSourceContainer((new File(gwtFile, "gwt-servlet.jar")).toString());
				} catch (JavaModelException e) {
					e.printStackTrace();
					break;
				}
				if (gwtContainer != null) {
					result.add(gwtContainer);
				}
				try {
					gwtContainer = getArchiveSourceContainer((new File(gwtFile, "gwt-user.jar")).toString());
				} catch (JavaModelException e) {
					e.printStackTrace();
					break;
				}
				if (gwtContainer != null) {
					result.add(gwtContainer);
				}
			}
		}

		return result.toArray(new ISourceContainer[result.size()]);
	}
	
	private void addProjectSourceContainers(IProject project, List<IRuntimeClasspathEntry> result) throws CoreException {
		if (project == null || !project.hasNature(JavaCore.NATURE_ID))
			return;

		IJavaProject jProject = JavaCore.create(project);
		result.add(JavaRuntime.newProjectRuntimeClasspathEntry(jProject));

		IClasspathEntry[] entries = jProject.getRawClasspath();
		for (int i = 0; i < entries.length; i++) {
			IClasspathEntry entry = entries[i];
			if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
				IRuntimeClasspathEntry rte = convertClasspathEntry(entry);
				if (rte != null)
					result.add(rte);
			}
		}
	}

	private IRuntimeClasspathEntry convertClasspathEntry(IClasspathEntry entry) {
		if (entry == null)
			return null;

		IPath srcPath = entry.getSourceAttachmentPath();
		if (srcPath != null && srcPath.segmentCount() > 0) {
			IRuntimeClasspathEntry rte = JavaRuntime.newArchiveRuntimeClasspathEntry(entry.getPath());
			rte.setSourceAttachmentPath(srcPath);
			rte.setSourceAttachmentRootPath(entry.getSourceAttachmentRootPath());
			return rte;
		}
		return null;
	}

	private ISourceContainer getArchiveSourceContainer(String location) throws JavaModelException {
		IWorkspaceRoot root = GdeUI.getWorkspace().getRoot();
		IFile[] containers = root.findFilesForLocation(new Path(location));
		for (int i = 0; i < containers.length; i++) {
			IJavaElement element = JavaCore.create(containers[i]);
			if (element instanceof IPackageFragmentRoot) {
				IPackageFragmentRoot archive = (IPackageFragmentRoot) element;
				IPath path = archive.getSourceAttachmentPath();
				if (path == null || path.segmentCount() == 0)
					continue;

				IPath rootPath = archive.getSourceAttachmentRootPath();
				boolean detectRootPath = rootPath != null && rootPath.segmentCount() > 0;

				IFile archiveFile = root.getFile(path);
				if (archiveFile.exists())
					return new ArchiveSourceContainer(archiveFile, detectRootPath);

				File file = path.toFile();
				if (file.exists())
					return new ExternalArchiveSourceContainer(file.getAbsolutePath(), detectRootPath);
			}
		}
		
		File locationFile = new File(location);
		if (locationFile.exists() && locationFile.isFile()) {
			return new ExternalArchiveSourceContainer(locationFile.getAbsolutePath(), true);
		}
		return null;
	}
}
